package com.harmonyos.cases.utils;

import com.harmonyos.cases.constants.Constants;
import com.harmonyos.cases.listener.GitListener;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationAction;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.codeStyle.CodeStyleManager;
import git4idea.commands.*;
import org.jetbrains.annotations.NotNull;

import java.awt.*;
import java.io.*;
import java.nio.file.*;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class InsertUtils {

    /**
     * 打开文件选择器
     *
     * @param project
     * @return
     */
    public static String openFileChooser(Project project) {
        FileChooserDescriptor descriptor = new FileChooserDescriptor(false, true, false, false, false, false)
                .withTitle("Select a Directory")
                .withDescription("Choose a Directory to Open")
                .withShowHiddenFiles(true);
        VirtualFile[] directories = FileChooser.chooseFiles(descriptor, project, null);
        if (directories.length > 0) {
            return directories[0].getCanonicalPath();
        }
        return null;
    }

    /**
     * 通过git下载case仓案例源代码
     *
     * @param project
     * @param downloadPath     下载代码的地址
     * @param casePath         case仓代码路径
     * @param caseRepositories case仓代码地址（https或者ssh地址）
     * @param gitListener
     */
    public static void downloadCode(@NotNull Project project, String downloadPath, String casePath, String caseRepositories, GitListener gitListener) {

        ApplicationManager.getApplication().executeOnPooledThread(() -> {

            notification(project, "Start download, please wait for a moment...", false, null);

            String[] temp = downloadPath.split("\\\\");
            String tempPath = getCacheDirectory().getAbsolutePath() + "\\" + temp[temp.length - 1];
            File tempFile = new File(tempPath);
            if (!tempFile.exists()) {
                tempFile.mkdirs();
            }
            VirtualFile dir = LocalFileSystem.getInstance().findFileByPath(tempPath);
            if (dir == null) {
                if (gitListener != null) {
                    gitListener.onGitResult("Directory not found");
                }
                return;
            }
            GitImpl gitImpl = new GitImpl();

            boolean result = executeGit(gitImpl, project, dir, GitCommand.INIT, gitListener);
            if (!result) {
                return;
            }

            result = executeGit(gitImpl, project, dir, GitCommand.CONFIG, gitListener, "core.sparsecheckout", "true");
            if (!result) {
                return;
            }

            File sparseCheckoutFile = new File(tempPath, ".git/info/sparse-checkout");
            try (FileWriter writer = new FileWriter(sparseCheckoutFile)) {
                writer.write(casePath + "\n");
            } catch (IOException e) {
                if (gitListener != null) {
                    gitListener.onGitResult("Write file failed");
                }
            }

            if (!isRemoteExist(gitImpl, project, dir, caseRepositories)) {
                result = executeGit(gitImpl, project, dir, GitCommand.REMOTE, gitListener, "add", "origin", caseRepositories);
                if (!result) {
                    return;
                }
            }

            result = executeGit(gitImpl, project, dir, GitCommand.PULL, gitListener, "origin", "master");
            if (result) {
                try {
                    tempPath = tempPath + "\\" + casePath.replaceAll("\\\\", "/");
                    copyDirectory(project, tempPath, downloadPath);
//                    deleteCode(project, downloadPath);
                    FileSystems.getDefault().provider().checkAccess(Paths.get(downloadPath.substring(0, downloadPath.lastIndexOf("\\"))));
//                    VirtualFileManager.getInstance().findFileByUrl().refresh(false, false);
                    notification(project, "Download succeed !", true, downloadPath.substring(0, downloadPath.lastIndexOf("\\")));
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

        });

    }

    private static void deleteCode(Project project, String path) {
        String[] deleteList = {"import { PluginConfig, etsGeneratorPlugin } from '@app/ets-generator';",
                "etsGeneratorPlugin(config)",
                "export * from './src/main/ets/generated/RouterBuilder';",
                "\"@ohos/base\": \"file:../../common/utils\",",
                "\"@ohos/dynamicsrouter\": \"file:../../common/routermodule\"",
                "import { AppRouter } from '@ohos/dynamicsrouter';",
                "@AppRouter({ name: \"videolistautoplay/VideoListAutoplay\" })"
        };

        readAndWriteFile(project, path + "/hvigorfile.ts", deleteList[0]);
        readAndWriteFile(project, path + "/hvigorfile.ts", deleteList[1]);

        readAndWriteFile(project, path + "/Index.ets", deleteList[2]);
        readAndWriteFile(project, path + "/oh-package.json5", deleteList[3]);
        readAndWriteFile(project, path + "/oh-package.json5", deleteList[4]);

        String result = readFile(project, path + "/hvigorfile.ts");
        if (result != null) {
            String temp = "const config: PluginConfig = {\n" +
                    "  scanFiles: [\"^*\"]";
//            result.indexOf(temp)+temp.length();
            String regex = "\"([^\"]*)\"";
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(result);
            if (matcher.find()) {
                String findText = matcher.group(1);
                readAndWriteFile(project, path + "/" + findText, deleteList[5]);
                String regex2 = "const config: PluginConfig = {\n" +
                        " scanFiles: [\"([^\"]*)\"";
            }
        }
    }

    private static String readFile(Project project, String filePath) {
        VirtualFile file = LocalFileSystem.getInstance().findFileByPath(filePath);
        if (file != null) {
            Document document = FileDocumentManager.getInstance().getDocument(file);
            if (document != null) {
                String result = document.getText();
                return result;
            }
        }
        return null;
    }

    private static void readAndWriteFile(Project project, String filePath, String... replaces) {
        VirtualFile file = LocalFileSystem.getInstance().findFileByPath(filePath);
        if (file != null) {
            Document document = FileDocumentManager.getInstance().getDocument(file);
            if (document != null) {
                String content = document.getText();
                if (replaces != null) {
                    for (String replace : replaces) {
                        content = content.replace(replace, "");
                    }
                }
                try {
                    String finalContent = content;
                    WriteCommandAction.runWriteCommandAction(project, () -> {
//                        document.deleteString(0, document.getTextLength());
                        commitDocumentChange(document, project);
                        document.replaceString(0, document.getTextLength(), finalContent);
                    });

                } catch (RuntimeException ex) {

                }

            }
        }
    }

    /**
     * 弹出通知
     *
     * @param project
     * @param message      通知信息
     * @param needOpenFile 是否需要打开文件
     * @param openFilePath 打开文件的路径（needOpenFile是false时，不需要传）
     */
    public static void notification(Project project, String message, boolean needOpenFile, String openFilePath) {
        Notification notification = new Notification("Add AkrUI Component", "Tips", "", NotificationType.INFORMATION);
        notification.setContent(message);
        if (needOpenFile && openFilePath != null && !openFilePath.isEmpty()) {
            notification.addAction(new NotificationAction("Open file") {
                @Override
                public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                    VirtualFile file = Objects.requireNonNull(LocalFileSystem.getInstance().findFileByPath(openFilePath));

                    if (file.isDirectory()) {
                        try {
                            Desktop.getDesktop().open(new File(openFilePath));
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        FileEditorManager.getInstance(project).openFile(file);
                    }

                }
            });
        }
        Notifications.Bus.notify(notification, project);
    }

    /**
     * git配置中远程仓库是否存在
     *
     * @param gitImpl
     * @param project
     * @param dir
     * @param remote  远程仓库地址
     * @return true-已存在远程仓，false-不存在远程仓
     */
    private static boolean isRemoteExist(GitImpl gitImpl, Project project, VirtualFile dir, String remote) {
        GitLineHandler handler = new GitLineHandler(project, dir, GitCommand.REMOTE);
        handler.addParameters("-v");
        GitCommandResult gitResult = gitImpl.runCommand(handler);
        List<String> remoteList = gitResult.getOutput();
        for (String remotePath : remoteList) {
            if (remotePath.contains(remote)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 执行git命令
     *
     * @param gitImpl
     * @param project
     * @param dir
     * @param gitCommand  git命令
     * @param gitListener
     * @param params      git命令的参数
     * @return
     */
    private static boolean executeGit(GitImpl gitImpl, Project project, VirtualFile dir, GitCommand gitCommand, GitListener gitListener, String... params) {
        GitLineHandler handler = new GitLineHandler(project, dir, gitCommand);
        if (params != null && params.length > 0) {
            handler.addParameters(params);
        }

//        handler.addLineListener(new GitLineHandlerListener() {
//            @Override
//            public void onLineAvailable(@NlsSafe String s, Key key) {
//                Notification notification = new Notification("Add AkrUI Component", "Tips", "", NotificationType.INFORMATION);
//                notification.setContent(s);
//                Notifications.Bus.notify(notification, project);
//            }
//        });
        GitCommandResult gitResult = gitImpl.runCommand(handler);

        int code = gitResult.getExitCode();
        if (code != 0) {
            if (gitListener != null) {
                gitListener.onGitResult(gitResult.getErrorOutput().get(0));
            }
            return false;
        }
        return true;
    }

    /**
     * 在光标处插入代码
     *
     * @param e    AnActionEvent对象
     * @param code 需要插入的代码块
     */
    public static void insertCodeAtCaret(@NotNull AnActionEvent e, String code) {
        if (code.isEmpty()) {
            return;
        }
        // 获取编辑器
        var editor = e.getData(CommonDataKeys.EDITOR);
        if (editor != null) {
            WriteCommandAction.runWriteCommandAction(editor.getProject(), () -> {
                var document = editor.getDocument();
                var caretModel = editor.getCaretModel();
                // 获取光标位置
                int caretOffset = caretModel.getOffset();
                // 在光标处插入代码
                document.insertString(caretOffset, code + "\n");
                reformatCode(document, Objects.requireNonNull(e.getProject()), e.getData(CommonDataKeys.VIRTUAL_FILE));
            });
        }
    }

    /**
     * 在当前文件中插入import代码
     *
     * @param e    AnActionEvent对象
     * @param code 需要插入的代码
     */
    public static void insertImportCode(@NotNull AnActionEvent e, String code) {
        if (code.isEmpty()) {
            return;
        }
        VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE);
        if (file != null) {
            PsiFile psiFile = PsiManager.getInstance(Objects.requireNonNull(e.getProject())).findFile(file);
            if (psiFile != null) {
                Document document = FileDocumentManager.getInstance().getDocument(file);
                if (document != null) {
                    WriteCommandAction.runWriteCommandAction(e.getProject(), () -> {
                        int offset = document.getText().indexOf("import");
                        if (offset != -1) {
                            document.insertString(offset, code + "\n");
                        } else {
                            document.insertString(0, code + "\n");
                        }
                    });
                }
            }
        }
    }

    /**
     * 添加依赖代码
     *
     * @param e       AnActionEvent对象
     * @param name    依赖的仓库名
     * @param version 依赖的仓库版本
     */
    public static void writeDependenciesCode(@NotNull AnActionEvent e, String name, String version) {
        if (name.isEmpty() || version.isEmpty()) {
            return;
        }
        // 在其他文件中写入代码
        // 这里可以用 VirtualFile 和 PsiFile 的相关API处理文件
        VirtualFile file = e.getData(CommonDataKeys.VIRTUAL_FILE);
        if (file != null) {
            String currentFilePath = file.getCanonicalPath();
            if (currentFilePath != null) {
                int index = currentFilePath.indexOf("src");
                if (index != -1) {
                    currentFilePath = currentFilePath.substring(0, index) + "oh-package.json5";
                    VirtualFile dependenciesFile = LocalFileSystem.getInstance().findFileByPath(currentFilePath);
                    if (dependenciesFile != null) {
                        Document document = FileDocumentManager.getInstance().getDocument(dependenciesFile);
                        if (document != null) {
                            String content = document.getText();
                            try {
                                JsonObject jsonObject = JsonParser.parseString(content).getAsJsonObject();
                                if (jsonObject.has("dependencies")) {
                                    JsonObject dependenciesObject = jsonObject.get("dependencies").getAsJsonObject();
                                    dependenciesObject.addProperty(name, version);
                                    jsonObject.add("dependencies", dependenciesObject);
                                    WriteCommandAction.runWriteCommandAction(e.getProject(), () -> {
                                        document.replaceString(0, document.getTextLength(), jsonObject.toString());
                                        reformatCode(document, Objects.requireNonNull(e.getProject()), dependenciesFile);
                                        Notification notification = new Notification(Constants.PLUGIN_TITLE, Constants.PLUGIN_NOTIFICATION_TIP, "Add dependencies in " + dependenciesFile.getCanonicalPath(), NotificationType.INFORMATION);
                                        notification.addAction(new NotificationAction(Constants.PLUGIN_NOTIFICATION_OPEN) {
                                            @Override
                                            public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                                                FileEditorManager.getInstance(e.getProject()).openFile(dependenciesFile);
                                            }
                                        });
                                        Notifications.Bus.notify(notification, e.getProject());

                                    });
                                }
                            } catch (RuntimeException ex) {
                                Notification notification = new Notification(Constants.PLUGIN_TITLE, Constants.PLUGIN_NOTIFICATION_ERROR, ex.getMessage() + "\nPlease check the json format", NotificationType.INFORMATION);
                                notification.addAction(new NotificationAction(Constants.PLUGIN_NOTIFICATION_OPEN) {
                                    @Override
                                    public void actionPerformed(@NotNull AnActionEvent anActionEvent, @NotNull Notification notification) {
                                        FileEditorManager.getInstance(Objects.requireNonNull(e.getProject())).openFile(dependenciesFile);
                                    }
                                });
                                Notifications.Bus.notify(notification, e.getProject());
                            }

                        }
                    }
                }
            }
        }
    }

    /**
     * 格式化指定文件代码
     *
     * @param document 如果有修改文件中的内容，需要传入修改文件时使用的Document对象，将修改内容提交到文件中，否则会报错。错误内容为Attempt to modify PSI for non-committed Document!
     * @param project  Project对象，可通过AnActionEvent对象获取
     * @param file     需要格式化的文件
     */
    private static void reformatCode(Document document, @NotNull Project project, VirtualFile file) {
        // 提交document的修改，否则格式化代码时会报错
        if (document != null) {
            commitDocumentChange(document, project);
        }

        PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
        if (psiFile != null) {
            // 格式化指定文件的代码
            CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
            codeStyleManager.reformat(psiFile);
        }
    }

    /**
     * 提交文档修改
     *
     * @param document
     * @param project
     */
    private static void commitDocumentChange(Document document, Project project) {
        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
        psiDocumentManager.commitDocument(document);
    }

    /**
     * 通过url获取文件名
     *
     * @param url
     * @return 文件名
     */
    public static @NotNull String getFileNameFromUrl(@NotNull String url) {
        return url.substring(url.lastIndexOf('/') + 1);
    }

    /**
     * 获取缓存路径
     *
     * @return 缓存路径
     */
    public static @NotNull File getCacheDirectory() {
        // 返回合适的缓存目录，例如用户的临时目录
        File file = new File(System.getProperty("java.io.tmpdir"), "AddArkUIComponent");
        if (!file.exists()) {
            file.mkdirs();
        }
        return file;
    }

    /**
     * 复制文件夹
     *
     * @param project
     * @param sourcePath 复制的原文件路径
     * @param targetPath 目标路径
     * @throws IOException
     */
    public static void copyDirectory(Project project, String sourcePath, String targetPath) throws IOException {
        File sourceDir = new File(sourcePath);
        File targetDir = new File(targetPath);

        // 创建目标目录
        if (!targetDir.exists()) {
            targetDir.mkdirs();
        }

        // 复制文件
        Files.walk(sourceDir.toPath()).forEach(source -> {
            Path target = targetDir.toPath().resolve(sourceDir.toPath().relativize(source));
            try {
                if (Files.isDirectory(source)) {
                    if (!Files.exists(target)) {
                        Files.createDirectory(target);
                    }
                } else {
                    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

    }
}
